package logs

import (
	"fmt"
	"github.com/shiena/ansicolor"
	"io"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

const (
	// UNKNOWN 定义日志级别
	UNKNOWN LogLevel = iota
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	FATAL
	layOut = "2006-01-02 15:04:05.000"
)

// LogLevel 日志级别
type LogLevel uint16

//Logger 日志结构体
type Logger struct {
	Level LogLevel
	wr    io.Writer
}

type callInfo struct {
	funcName, fileName string
	lineNo             int
}

var log Logger

func init() {
	SetLogLevel(FATAL)
}

// SetLogLevel 向终端写日志相关内容
//NewLog 构造函数
func SetLogLevel(level LogLevel) {
	log = Logger{Level: level, wr: ansicolor.NewAnsiColorWriter(os.Stderr)}
}

func Debug(f interface{}, v ...interface{}) {
	log.debug(formatLog(f, v...), getInfo(2))
}

func Info(f interface{}, v ...interface{}) {
	log.info(formatLog(f, v...), getInfo(2))
}

func Warn(f interface{}, v ...interface{}) {
	log.warning(formatLog(f, v...), getInfo(2))
}

func Error(f interface{}, v ...interface{}) {
	log.error(formatLog(f, v...), getInfo(2))
}

func Fatal(f interface{}, v ...interface{}) {
	log.fatal(formatLog(f, v...), getInfo(2))
}

func (l Logger) debug(msg string, callInfo callInfo) {
	if l.Level >= DEBUG {
		now := time.Now()
		l.wr.Write([]byte(fmt.Sprintf("%s %s %s:%d: %s\n", now.Format(layOut), colors[3]("[D]"), callInfo.funcName, callInfo.lineNo, msg)))
	}
}
func (l Logger) info(msg string, callInfo callInfo) {
	if l.Level >= INFO {
		now := time.Now()
		l.wr.Write([]byte(fmt.Sprintf("%s %s %s:%d: %s\n", now.Format(layOut), colors[2]("[I]"), callInfo.funcName, callInfo.lineNo, msg)))
	}
}
func (l Logger) warning(msg string, callInfo callInfo) {
	if l.Level >= WARNING {
		now := time.Now()
		l.wr.Write([]byte(fmt.Sprintf("%s %s %s:%d: %s\n", now.Format(layOut), colors[1]("[W]"), callInfo.funcName, callInfo.lineNo, msg)))
	}
}
func (l Logger) error(msg string, callInfo callInfo) {
	if l.Level >= ERROR {
		now := time.Now()
		l.wr.Write([]byte(fmt.Sprintf("%s %s %s:%d: %s\n", now.Format(layOut), colors[0]("[E]"), callInfo.funcName, callInfo.lineNo, msg)))
	}
}
func (l Logger) fatal(msg string, callInfo callInfo) {
	if l.Level >= FATAL {
		now := time.Now()
		l.wr.Write([]byte(fmt.Sprintf("%s %s %s:%d: %s\n", now.Format(layOut), colors[0]("[F]"), callInfo.funcName, callInfo.lineNo, msg)))
	}
}

//获取行号等信息
func getInfo(skip int) (callInfo callInfo) {
	pc, file, lineNo, ok := runtime.Caller(skip)
	if !ok {
		fmt.Printf("有误\n")
		return
	}
	callInfo.lineNo = lineNo
	callInfo.funcName = runtime.FuncForPC(pc).Name()
	callInfo.fileName = path.Base(file)
	return
}

func formatLog(f interface{}, v ...interface{}) string {
	var msg string
	switch f.(type) {
	case string:
		msg = f.(string)
		if len(v) == 0 {
			return msg
		}
		if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") {
			//format string
		} else {
			//do not contain format char
			msg += strings.Repeat(" %v", len(v))
		}
	default:
		msg = fmt.Sprint(f)
		if len(v) == 0 {
			return msg
		}
		msg += strings.Repeat(" %v", len(v))
	}
	return fmt.Sprintf(msg, v...)
}

// newBrush return a fix color Brush
func newBrush(color string) brush {
	pre := "\033["
	reset := "\033[0m"
	return func(text string) string {
		return pre + color + "m" + text + reset
	}
}

// brush is a color join function
type brush func(string) string

var colors = []brush{
	newBrush("1;31"), // Error              red
	newBrush("1;33"), // Warning            yellow
	newBrush("1;34"), // Informational      blue
	newBrush("1;44"), // Debug              Background blue
}
